import request, { TRequestOptins } from "@/utils/request";
import Taro from "@tarojs/taro";
import { ref, Ref, watch, unref } from "vue";

const parse = (url = "") => url.split(" ") as ["GET" | "POST", string];

export type TUseFetchOptions<TData = any> = {
  showLoading?: boolean;
  loadingTitle?: string;

  refetch?: boolean; // URL刷新自动请求
  immediate?: boolean;

  initialData?: TData | Ref<TData>;
  defaultParams?: object;

  beforeFetch?: (options: any) => Promise<any> | any;
  afterFetch?: (res: TData) => any;

  onSuccess?: (data: TData, originData?: any) => void;
  onError?: (err: any) => void;
  onFinally?: () => void;
};

type TUseFetchReturn<TData> = {
  data: Ref<TData>;
  error: Ref<any>;
  loading: Ref<boolean>;
  run: (params?: Record<string, any>) => Promise<TUseFetchReturn<TData>>;
  refresh: () => TUseFetchReturn<TData>["run"];
};

type TUseFetchReturnPromise<TData> = TUseFetchReturn<TData> &
  Promise<TUseFetchReturn<TData>>;

function useFetch<TData>(
  url: string | Ref<string>,
  {
    showLoading,
    loadingTitle = "正在加载",

    refetch = false,
    immediate = true,
    defaultParams,
    initialData,
    onSuccess,
    onFinally,
    onError,
    afterFetch = (e) => e,
    beforeFetch = (e) => e,
  }: TUseFetchOptions<TData> = {},
  requeseOptins?: TRequestOptins
): Promise<TUseFetchReturn<TData>> & TUseFetchReturn<TData> {
  const refURL = ref(url);

  const data = ref<TData | undefined>(initialData as TData);
  const loading = ref(false);
  const error = ref();
  const params = ref(defaultParams);

  const run = async (fetchParams) => {
    const [url] = parse(unref(refURL) as string);
    const payload = await beforeFetch({
      method: "POST",
      url,
      data: fetchParams,
    });

    // 发送请求
    loading.value = true;
    if (showLoading) {
      Taro.showLoading({ title: loadingTitle });
    }

    params.value = payload.data;
    return request(payload, requeseOptins)
      .then((res: any) => {
        error.value = undefined;
        data.value = afterFetch(res);
        onSuccess?.(data.value, res);

        return res;
      })
      .then(() => ({
        data,
        error,
        loading,
        run,
      }))
      .catch((err) => {
        data.value = undefined;
        error.value = false;
        onError?.(err);
        return Promise.reject(err);
      })
      .finally(() => {
        if (showLoading) {
          Taro.hideLoading();
        }

        loading.value = false;
        onFinally?.();
      }) as Promise<TData>;
  };

  const refresh = () => run(unref(params));

  // URL变化重新刷新
  watch(refURL, () => refetch && run(params));

  // 是否立即执行
  const promise = immediate && url ? run(unref(params)) : {};

  return Object.assign(promise, {
    data,
    error,
    loading,
    run,
    refresh,
  }) as unknown as TUseFetchReturnPromise<TData>;
}

export default useFetch;
